package org.xxpay.pay.channel.ysepay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.xxpay.core.common.domain.api.msg.AgentPayBalanceRetMsg;
import org.xxpay.core.common.util.AmountUtil;
import org.xxpay.core.common.util.DateUtil;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.entity.BankCardBin;
import org.xxpay.core.entity.TransOrder;
import org.xxpay.core.common.domain.api.msg.QueryRetMsg;
import org.xxpay.pay.channel.BaseTrans;
import org.xxpay.pay.channel.ysepay.util.Https;
import org.xxpay.pay.channel.ysepay.util.SignUtils;
import org.xxpay.pay.service.CommonService;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 银盛代付发起代付， 订单查询， 账户余额查询
 */
@Service
public class YsepayTransService extends BaseTrans {

    private static final MyLog logger = MyLog.getLog(YsepayTransService.class);

    @Autowired
    protected CommonService commonService;

    @Override
    public String getChannelName() {
        return YsepayConfig.CHANNEL_NAME_YSEPAY;
    }

    @Override
    public QueryRetMsg trans(TransOrder transOrder) {
        String logPrefix = "【银盛代付】";

        String channelErrCode = "";  //记录上游错误代码
        String channelErrMsg = "";  //记录上游错误信息

        try {
            // [01]. 获取订单和基本配置信息
            String transOrderId = transOrder.getTransOrderId();
            YsepayConfig ysepayConfig = new YsepayConfig(getTransParam(transOrder));  //银盛通道配置信息
            Date nowTime = new Date();  //获取当前的时间戳

            //[02]. 封装请求参数
            Map<String, String> reqParamMap = new HashMap<>();
            reqParamMap.put("method", "ysepay.df.single.quick.accept");	//接口名称：代付单笔加急
            reqParamMap.put("partner_id", ysepayConfig.getSellerId()); //商户在银盛支付平台开设的用户号[商户号]
            reqParamMap.put("charset", YsepayConfig.CHARSET); //商户网站使用的编码格式
            reqParamMap.put("sign_type", "RSA"); //RSA
            reqParamMap.put("timestamp", DateUtil.date2Str(nowTime)); //发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
            reqParamMap.put("version", "3.0"); //接口版本
            reqParamMap.put("notify_url", super.getNotifyTransUrl(getChannelName(), null)); //异步通知地址
            //业务参数信息
            JSONObject bizContentJSON = new JSONObject();
            bizContentJSON.put("out_trade_no", transOrderId);
            bizContentJSON.put("shopdate", DateUtil.date2Str(nowTime, DateUtil.FORMAT_YYYY_MM_DD2));
            bizContentJSON.put("business_code", ysepayConfig.getBusinessCode());
            bizContentJSON.put("currency", "CNY");
            bizContentJSON.put("total_amount", AmountUtil.convertCent2Dollar(transOrder.getAmount().toString()));
            bizContentJSON.put("subject", StringUtils.isEmpty(transOrder.getRemarkInfo()) ? transOrderId : transOrder.getRemarkInfo());
            bizContentJSON.put("bank_name", ysepayConfig.getSellerId());
            bizContentJSON.put("seller_name", ysepayConfig.getSellerName());
            bizContentJSON.put("timeout_express","2h");
            BankCardBin bankCardBin = commonService.bankCardBinService.findByCardNo(transOrder.getAccountNo());
            bizContentJSON.put("bank_name", bankCardBin == null ? "" : bankCardBin.getBankName().replaceAll("\n",""));
            bizContentJSON.put("bank_city", "北京市");
            bizContentJSON.put("bank_account_no", transOrder.getAccountNo());
            bizContentJSON.put("bank_account_name", transOrder.getAccountName());
            bizContentJSON.put("bank_account_type", "personal");
            bizContentJSON.put("bank_card_type", "debit");
            reqParamMap.put("biz_content", bizContentJSON.toJSONString());

            //[03]. 获取证书文件并签名
            String privatePfxFilePath = payConfig.getCertRootPath() + File.separator + getChannelName() + File.separator + ysepayConfig.getPrivatePfxPath();
            reqParamMap.put("sign", SignUtils.rsaSign(reqParamMap, privatePfxFilePath, ysepayConfig.getPfxPwd()));

            //[04]. 请求接口
            logger.info("{} ysepay Trans req reqParamMap: {}", logPrefix, reqParamMap);
            String resStr = Https.httpsSend(YsepayConfig.AGENTPAY_URL, reqParamMap);
            logger.info("{} ysepay Trans resData: {}", logPrefix, resStr);

            //[05]. 验证返回数据 （是否为空，验签是否通过）
            if(StringUtils.isEmpty(resStr)){
                throw new Exception("返回数据为null");
            }
            JSONObject resJSON = JSON.parseObject(resStr, Feature.OrderedField); //保持原始对象顺序
            String resSign = resJSON.getString("sign");  //返回数据的签名
            JSONObject resBizJSON = resJSON.getJSONObject("ysepay_df_single_quick_accept_response");
            //银盛公钥文件
            String publicCerFilePath = payConfig.getCertRootPath() + File.separator + getChannelName() + File.separator + ysepayConfig.getPublicCerPath();
            boolean isTrue = SignUtils.rsaCheckContent(publicCerFilePath, resBizJSON.toJSONString(), resSign);
            if(!isTrue){
                throw new Exception("验签失败！");
            }

            //[06]. 处理返回数据
            String code = resBizJSON.getString("code");
            if(!"10000".equals(code)){
                channelErrCode = code + "|" +resBizJSON.getString("sub_code");;
                channelErrMsg = resBizJSON.getString("msg") + "|" +resBizJSON.getString("sub_msg");
                throw new Exception("请求代付失败！");
            }

            String tradeStatus = resBizJSON.getString("trade_status");
            if("TRADE_ACCEPT_SUCCESS".equals(tradeStatus)){ //受理成功， 需要再次查询
                logger.info("{} >>> 处理中，需要再次查询", logPrefix);
                return QueryRetMsg.waiting();
            }

            if("TRADE_SUCCESS".equals(tradeStatus)){ //交易成功
                logger.info("{} >>> 明确成功", logPrefix);
                return QueryRetMsg.confirmSuccess(null);
            }

            if("TRADE_FAILURE".equals(tradeStatus) || "DISHONOUR_SUCCESS".equals(tradeStatus)){  //交易失败 || 退票成功
                logger.info("{} >>> 明确失败", logPrefix);
                return QueryRetMsg.confirmFail();
            }

            logger.info("{} >>> 结果未知, 等到下次查询。 tradeStatus={}", logPrefix, tradeStatus);
            return QueryRetMsg.unknown();

        } catch (Exception e) {
            logger.error("{}转账异常, channelErrCode={}, channelErrMsg={}", logPrefix, e, channelErrCode, channelErrMsg);
            return QueryRetMsg.sysError();
        }

    }

    public QueryRetMsg query(TransOrder transOrder) {
        String logPrefix = "【银盛代付查询】";
        QueryRetMsg retObj ;

        String channelErrCode = "";  //记录上游错误代码
        String channelErrMsg = "";  //记录上游错误信息
        String channelOriginResponse = null; //用户运营平台查询上游结果使用

        try {
            // [01]. 获取订单和基本配置信息
            String transOrderId = transOrder.getTransOrderId();
            YsepayConfig ysepayConfig = new YsepayConfig(getTransParam(transOrder));  //银盛通道配置信息
            Date nowTime = new Date();  //获取当前的时间戳

            //[02]. 封装请求参数
            Map<String, String> reqParamMap = new HashMap<>();
            reqParamMap.put("method", "ysepay.df.single.query");	//接口名称：代付查询
            reqParamMap.put("partner_id", ysepayConfig.getSellerId()); //商户在银盛支付平台开设的用户号[商户号]
            reqParamMap.put("charset", YsepayConfig.CHARSET); //商户网站使用的编码格式
            reqParamMap.put("sign_type", "RSA"); //RSA
            reqParamMap.put("timestamp", DateUtil.date2Str(nowTime)); //发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
            reqParamMap.put("version", "3.0"); //接口版本
            //业务参数信息
            JSONObject bizContentJSON = new JSONObject();
            bizContentJSON.put("out_trade_no", transOrderId);
            bizContentJSON.put("shopdate", DateUtil.date2Str(nowTime, DateUtil.FORMAT_YYYY_MM_DD2));
            reqParamMap.put("biz_content", bizContentJSON.toJSONString());

            //[03]. 获取证书文件并签名
            String privatePfxFilePath = payConfig.getCertRootPath() + File.separator + getChannelName() + File.separator + ysepayConfig.getPrivatePfxPath();
            reqParamMap.put("sign", SignUtils.rsaSign(reqParamMap, privatePfxFilePath, ysepayConfig.getPfxPwd()));

            //[04]. 请求接口
            logger.info("{} ysepay Trans req reqParamMap: {}", logPrefix, reqParamMap);
            String resStr = Https.httpsSend(YsepayConfig.AGENTPAY_QUERY_URL, reqParamMap);
            logger.info("{} ysepay Trans resData: {}", logPrefix, resStr);

            //[05]. 验证返回数据 （是否为空，验签是否通过）
            if(StringUtils.isEmpty(resStr)){
                throw new Exception("返回数据为null");
            }
            JSONObject resJSON = JSON.parseObject(resStr, Feature.OrderedField); //保持原始对象顺序
            String resSign = resJSON.getString("sign");  //返回数据的签名
            JSONObject resBizJSON = resJSON.getJSONObject("ysepay_df_single_query_response");
            //银盛公钥文件
            String publicCerFilePath = payConfig.getCertRootPath() + File.separator + getChannelName() + File.separator + ysepayConfig.getPublicCerPath();
            boolean isTrue = SignUtils.rsaCheckContent(publicCerFilePath, resBizJSON.toJSONString(), resSign);
            if(!isTrue){
                throw new Exception("验签失败！");
            }

            //[06]. 处理返回数据
            channelOriginResponse = resBizJSON.toJSONString(); //上游返回信息
            String code = resBizJSON.getString("code");
            if(!"10000".equals(code)){
                channelErrCode = code + "|" +resBizJSON.getString("sub_code");
                channelErrMsg = resBizJSON.getString("msg") + "|" +resBizJSON.getString("sub_msg");
                throw new Exception("查询代付订单失败， 状态非10000！");
            }

            String tradeStatus = resBizJSON.getString("trade_status");
            if("TRADE_ACCEPT_SUCCESS".equals(tradeStatus)){ //受理成功， 需要再次查询
                logger.info("{} >>> 处理中，需要再次查询", logPrefix);
                retObj = QueryRetMsg.waiting();
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;

            }

            if("TRADE_SUCCESS".equals(tradeStatus)){ //交易成功
                logger.info("{} >>> 明确成功", logPrefix);
                retObj = QueryRetMsg.confirmSuccess(null);
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;

            }

            if("TRADE_FAILURE".equals(tradeStatus) || "DISHONOUR_SUCCESS".equals(tradeStatus)){  //交易失败 || 退票成功
                logger.info("{} >>> 明确失败", logPrefix);
                retObj = QueryRetMsg.confirmFail();
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;
            }

            logger.info("{} >>> 结果未知, 等到下次查询。 tradeStatus={}", logPrefix, tradeStatus);
            retObj = QueryRetMsg.unknown();
            retObj.setChannelOriginResponse(channelOriginResponse);
            return retObj;

        } catch (Exception e) {
            logger.error("{} 异常, channelErrCode={}, channelErrMsg={}", logPrefix, channelErrCode, channelErrMsg, e);
            retObj = QueryRetMsg.sysError();
            retObj.setChannelOriginResponse(channelOriginResponse);
            return retObj;
        }
    }


    @Override
    public AgentPayBalanceRetMsg balance(String payParam) {

        String logPrefix = "【银盛余额查询】";
        JSONObject retObj = buildRetObj();
        try {
            // [01]. 获取基本配置信息
            YsepayConfig ysepayConfig = new YsepayConfig(payParam); //获取配置信息
            Date nowTime = new Date();  //获取当前的时间戳

            //[02]. 封装请求参数
            Map<String, String> reqParamMap = new HashMap<>();
            reqParamMap.put("partner_id", ysepayConfig.getSellerId()); //商户在银盛支付平台开设的用户号[商户号]
            reqParamMap.put("method", "ysepay.online.user.account.get");	//接口名称：账户余额查询
            reqParamMap.put("charset", YsepayConfig.CHARSET); //商户网站使用的编码格式
            reqParamMap.put("sign_type", "RSA"); //RSA
            reqParamMap.put("timestamp", DateUtil.date2Str(nowTime)); //发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
            reqParamMap.put("version", "3.0"); //接口版本
            //业务参数信息
            JSONObject bizContentJSON = new JSONObject();
            bizContentJSON.put("user_code", ysepayConfig.getSellerId());
            bizContentJSON.put("user_name", ysepayConfig.getSellerName());
            reqParamMap.put("biz_content", bizContentJSON.toJSONString());

            //[03]. 获取证书文件并签名
            String privatePfxFilePath = payConfig.getCertRootPath() + File.separator + getChannelName() + File.separator + ysepayConfig.getPrivatePfxPath();
            reqParamMap.put("sign", SignUtils.rsaSign(reqParamMap, privatePfxFilePath, ysepayConfig.getPfxPwd()));

            //[04]. 请求接口
            logger.info("{} ysepay Trans req reqParamMap: {}" ,logPrefix, reqParamMap);
            String resStr = Https.httpsSend(YsepayConfig.API_URL, reqParamMap);
            logger.info("{} ysepay Trans resData: {}", logPrefix, resStr);

            //[05]. 验证返回数据 （是否为空，验签是否通过）
            if(StringUtils.isEmpty(resStr)){
                throw new Exception("返回数据为null");
            }
            JSONObject resJSON = JSON.parseObject(resStr, Feature.OrderedField); //保持原始对象顺序
            String resSign = resJSON.getString("sign");  //返回数据的签名
            JSONArray resBizJSON = resJSON.getJSONArray("ysepay_online_user_account_get_response");
            String publicCerFilePath = payConfig.getCertRootPath() + File.separator + getChannelName() + File.separator + ysepayConfig.getPublicCerPath();
            boolean isTrue = SignUtils.rsaCheckContent(publicCerFilePath, resBizJSON.toJSONString(), resSign);
            if(!isTrue){
                throw new Exception("验签失败！");
            }

            //[06]. 处理返回数据
            JSONObject balanceObject = null;
            for(Object obj : resBizJSON){
                JSONObject jsonObject = (JSONObject)obj;
                if(ysepayConfig.getAgentpayAccountId().equals(jsonObject.getString("account_no"))){
                    balanceObject = jsonObject;
                }
            }

            if(balanceObject == null){ //未查询到代付账户
                logger.error("{}代付账户配置信息有误，未查询到代付账户！", logPrefix);
                return AgentPayBalanceRetMsg.fail();
            }

            return AgentPayBalanceRetMsg.success(
                    Long.parseLong(AmountUtil.convertDollar2Cent(balanceObject.get("available_amount").toString())), // 现金余额
                    Long.parseLong(AmountUtil.convertDollar2Cent(balanceObject.get("available_amount").toString())) // 代付可用余额
            );

        }catch (Exception e) {
            logger.error("{}查询头寸异常", logPrefix, e);
            return AgentPayBalanceRetMsg.fail();
        }
    }
}
